<template>
  <div class="default">
    <h3>Vuex</h3>
    <p>vue2.x --> vuex3.x</p>
    <p>vue3.x --> vuex4.x</p>
    <hr>

    <h3>store</h3>
    <p>通过new Vuex.Store()方法创建，用于存储状态</p>
    <ul class="ul1">
      <li>- 创建的store引入到main.js中，并放在new Vue({ store })中，store实例会被注入到所有组件</li>
      <li>- store.state 获取状态对象</li>
      <li>- store.commit 触发变更状态</li>
      <li>- this.$store 组件中获取和使用store</li>
    </ul>
    <hr>

    <h3>state</h3>
    <p>一个对象，例如{ count: 0 }</p>
    <p>可通过store获取该对象，store.state。或获取具体值store.state.count</p>
    <hr>

    <h3>mapState</h3>
    <p>从vuex中引入import { mapState } from 'vuex'</p>
    <p>用于同时获取多个状态值，返回包含这些状态的状态对象，状态对象会被映射到计算属性中</p>
    <p>此时就不需要通过store.state.count获取，而是可以直接this.count获取</p>
    <p>写法一：computed: { ...mapState({ count: 'count' }) }</p>
    <p>写法二：computed: { ...mapState(['count']) }</p>
    <p>如果在组件中想用别的名称替代，用this.num来获取count的值，而不像用this.count，可以这样写</p>
    <p>computed: { ...mapState({ num: 'count' }) }</p>
    <hr>

    <h3>getters</h3>
    <p>是一个对象，state的状态派生，相当于store的计算属性</p>
    <p>写法：getters: { doneTodos(state) { return state.todos.filter(v => v.id == 1) } }</p>
    <p>可以通过store.getters获取getters对象</p>
    <hr>

    <h3>mapGetters</h3>
    <p>和mapState类似写法，mapGetters获取的是不同的getter属性</p>
    <p>写法一：computed: { ...mapGetters({ doneTodos: 'doneTodos' }) }</p>
    <p>写法二：computed: { ...mapGetters(['doneTodos']) }</p>
    <p>此时在组件中同样不需要通过store.getters获取，而是可以直接this.doneTodos获取</p>
    <hr>

    <h3>mutations</h3>
    <p>更改状态的<strong>唯一方法</strong>就是提交mutation</p>
    <p>mutations是一个对象，包含了多个更改状态的方法mutation</p>
    <p>每个mutation必须是同步函数，并且mutation中不处理异步方法</p>
    <p>写法：mutations: { increment(state) { state.count++ } }</p>
    <p>改变状态：store.commit('increment')</p>
    <p>每个mutation还可以带另一个参数payload，也是处理state变更的</p>
    <p>payload可以是单个值，也可以是一个对象</p>
    <p>mutations: { increment(state, n) { state.count += n } }</p>
    <p>改变状态：store.commit('increment', 10)</p>
    <p>mutations: { increment(state, payload) { state.count += payload.n } }</p>
    <p>改变状态：store.commit('increment', { n: 10 })</p>
    <p>也可以这样提交，此时payload默认是一个对象：</p>
    <p>改变状态：store.commit({ type: 'increment', n: 10 })</p>
    <hr>

    <h3>mapMutations</h3>
    <p>在组件中使用，写法都类似，只不过它是写在methods中的</p>
    <p>写法一：methods: { ...mapMutations({ increment: 'increment' }) }</p>
    <p>写法二：methods: { ...mapMutations(['increment']) }</p>
    <p>用法也类似，不再需要写this.$store.commit('increment')，而是直接this.increment(xxx)</p>
    <hr>

    <h3>actions</h3>
    <p>actions也是一个对象，包含了很多个action方法。action方法用于处理异步方法，并提交mutation</p>
    <p>action接受一个和store实例具有相同属性和方法的对象context为参数</p>
    <p>写法：actions: { increment(context) { context.commit('increment') } }</p>
    <p>也可以用解构语法：{ increment({ commit }) { commit('increment') } }</p>
    <p>通过dispatch分发action，触发action的执行</p>
    <p>store.dispatch('increment')</p>
    <p>action也有第二个参数payload，分发形式也有多种写法</p>
    <p>store.dispatch('increment', 10)</p>
    <p>store.dispatch('increment', { n: 10 })</p>
    <p>store.dispatch({ type: 'increment', n: 10 })</p>
    <hr>

    <h3>async actions</h3>
    <p>action的重要作用是处理异步函数，比如接口调用等</p>
    <p>通常是这样：actions: { actionA({ commit }) { return new Promise((resolve, reject) => {...})} }</p>
    <p>调用分发：store.dispatch('actionA').then(...)</p>
    <hr>

    <h3>mapActions</h3>
    <p>在组件中使用，写法类似于mapMutations，也是写在methods中</p>
    <p>写法一：methods: { ...mapActions({ increment: 'increment' }) }</p>
    <p>写法二：methods: { ...mapActions(['increment']) }</p>
    <p>用法也类似，不再需要写this.$store.dispatch('increment')，而是直接this.increment(xxx)</p>
    <hr>

    <h3>modules</h3>
    <p>对store进行分割，形成不同的store模块，可以实现业务场景分离</p>
    <p>
      const moduleA = {<br/>
        state: () => ({ ... }),<br/>
        mutations: { ... },<br/>
        actions: { ... },<br/>
        getters: { ... }<br/>
      }<br/>
      <br/>
      const moduleB = {<br/>
        state: () => ({ ... }),<br/>
        mutations: { ... },<br/>
        actions: { ... }<br/>
      }<br/>
      <br/>
      const store = new Vuex.Store({<br/>
        modules: {<br/>
          a: moduleA,<br/>
          b: moduleB<br/>
        }<br/>
      })<br/>
      <br/>
      store.state.a // -> moduleA 的状态<br/>
      store.state.b // -> moduleB 的状态
    </p>
  </div>
</template>

<script>
export default {
  name: 'DefaultVuex',
}
</script>

<style lang="less" scoped>
.ul1 {
  box-sizing: border-box;
  padding-left: 1em;
}
</style>